import { PerspectiveCamera } from "three/src/cameras/PerspectiveCamera";
import { App } from "../../application";
import { Plane } from "three/src/math/Plane";
import { Vector3 } from "three/src/math/Vector3";
import { Ray } from "three/src/math/Ray";
import GeoUtils from "../../utils/GeoUtils";
import { MathUtils } from "three/src/math/MathUtils";
import { Point } from "../../Engine";
import Const from "../../application/constants/Const";
/**
 * 根据相机，计算地图的层级和可视范围
 */
class MapLevelController {
    private virturalCamera: PerspectiveCamera;
    private vituralViewportWidth: number;
    private vituralViewportHeight: number;
    // private virturalScreenArea: number;
    private app: App;
    private mapPlane: Plane;;
    private TileNum = 6; //假定每个层级加载6个图片，在代码中用这个值计算每个层级的可视范围
    //这个数组的值是通过getVisuralScope函数计算得到的，预缓冲了这些值，就可以在getScopeArray函数中直接使用，而不用每次都计算，可以提高效率
    private scopeArray = [240450100.1134078, 120225050.0567039, 60112525.02835195, 30056262.514175974, 15028131.257087987, 7514065.628543993, 3757032.8142719967, 1878516.4071359984, 939258.2035679992, 469629.1017839996, 234814.5508919998, 117407.2754459999, 58703.63772299995, 29351.818861499974, 14675.909430749987, 7337.954715374994, 3668.977357687497, 1834.4886788437484, 917.2443394218742, 458.6221697109371, 229.31108485546855, 114.65554242773428, 57.32777121386714];

    public constructor() {
        this.app = App.getInstance();

        // let size = this.app.getSize();
        this.vituralViewportWidth = 1528; //size.getWidth(); //
        this.vituralViewportHeight = 742; //size.getHeight();
        // let virtruralCameraAspect = this.vituralViewportWidth / this.vituralViewportHeight;
        // this.virturalScreenArea = this.vituralViewportWidth * this.vituralViewportHeight;

        let aspect = this.vituralViewportWidth / this.vituralViewportHeight;
        this.virturalCamera = new PerspectiveCamera(80, aspect, 1, 200000000);
        this.virturalCamera.up.set(0, 0, 1);

        this.mapPlane = new Plane(new Vector3(0, 0, 1), 0);

        // this.scopeArray = this.getScopeArray(6);
    }

    public getResArray() {//得到每个地图层级像素的分辨率
        let resolutionArray = [];
        let mapConfig = App.getInstance().options.Map;
        for (let i = 0; i < mapConfig.ScaleList.length; i++) {
            let scale = mapConfig.ScaleList[i];
            let res = GeoUtils.getResolution(scale); //TileUtil.getResolution(scale);
            resolutionArray.push(res);
        }
        return resolutionArray;
    }

    public getScopeArray(tileNum = this.TileNum) {//得到每个地图层级的可视范围
        let scopeArray = [];
        let mapConfig = App.getInstance().options.Map;
        for (let i = 0; i < mapConfig.ScaleList.length; i++) {
            let scale = mapConfig.ScaleList[i];
            let res = GeoUtils.getResolution(scale);
            let levelScope = res * mapConfig.TileSize * tileNum;
            scopeArray.push(levelScope);
        }
        return scopeArray;
    }

    public getVisuralScope() {//得到当前的可视范围
        this.virturalCamera.position.copy(this.app.three.camera.position);
        this.virturalCamera.updateMatrix();
        this.virturalCamera.updateMatrixWorld();
        this.virturalCamera.updateProjectionMatrix();

        //把屏幕四个角的点转换到视椎体的近平面上，然后从相机位置发射射线，找到各自在底图上的相交点
        let leftTopPoint = this.convertScreenToThree(0, 0, 1);
        let leftTopVector = leftTopPoint.clone().sub(this.virturalCamera.position.clone()).normalize();
        leftTopPoint = this.getIntersetPoint(this.mapPlane, leftTopVector);

        let rightTopPoint = this.convertScreenToThree(this.vituralViewportWidth, 0, 1);
        let rightTopVector = rightTopPoint.clone().sub(this.virturalCamera.position.clone()).normalize();
        rightTopPoint = this.getIntersetPoint(this.mapPlane, rightTopVector);

        let leftBottomPoint = this.convertScreenToThree(0, this.vituralViewportHeight, 1);
        let leftBottomVector = leftBottomPoint.clone().sub(this.virturalCamera.position.clone()).normalize();
        leftBottomPoint = this.getIntersetPoint(this.mapPlane, leftBottomVector);

        // let rightBottomPoint = this.convertScreenToThree(this.vituralViewportWidth, this.vituralViewportHeight);
        // let rightBottomVector = rightBottomPoint.clone().sub(this.virturalCamera.position.clone()).normalize();
        // rightBottomPoint = this.getIntersetPoint(this.mapPlane, rightBottomVector);

        let visuralWidtdh = leftTopPoint.clone().distanceTo(rightTopPoint);
        let visuralHeight = leftTopPoint.clone().distanceTo(leftBottomPoint);
        return visuralHeight > visuralWidtdh ? visuralHeight : visuralWidtdh;

        /*
        let visualMapArea = visuralWidtdh * visuralHeight;
        let resolution = visualMapArea / this.virturalScreenArea;

        let maxZoom = Number(this.app.options.Map.maxZoom);
        let minZoom = Number(this.app.options.Map.minZoom);

        let mapLevel = minZoom;
        if(resolution >= this.resolutionArray[minZoom]){
            return mapLevel;
        }
        if(resolution <= this.resolutionArray[maxZoom]){
            mapLevel = maxZoom;
            return mapLevel;
        }

        let lastResolution = this.resolutionArray[0];
        for(let i=1; i<this.resolutionArray.length; i++){
            let currentResolution = this.resolutionArray[i];
            if(resolution < lastResolution && resolution >= currentResolution){
                mapLevel = i;
                break;
            }
        }

        return mapLevel;
        */
    }

    /**
     * 获取当前地图的层级
     * @param isConsiderLevelRange 是否考虑地图的层级范围
     */
    public computeMapLevel(isConsiderLevelRange = true) {
        let maxZoom = Number(this.app.options.Map.maxZoom);
        let minZoom = Number(this.app.options.Map.minZoom);

        // let fovRad = MathUtils.degToRad(this.app.options.Camera.cameraFov);
        let visualScope = this.getVisuralScope();
        visualScope = visualScope * (this.app.options.Camera.cameraFov / 55); //Math.sin(fovRad);//
        let mapLevel = this.app.map.zoom;
        if (visualScope == 0) {
            return mapLevel;
        }

        let angle = this.app.map.getPitchAngle(); //理论上这里应该乘以0.8,这是为了在倾斜地图的时候得出不同的层级，但是这样做会导致地图闪动一下，视觉效果不好。经过排查发现，是Orbit类倾斜的时候，相机的高度并没有变低，而是变高了；相机和地图垂直的时候，相机高度变低了。因此这里很麻烦，先不要乘以0.8
        let expectScopeFactor = Number(Math.cos(MathUtils.degToRad(angle)).toFixed(4));
        /*let mapConfig = App.getInstance().options.Map;
        for (let i = 0; i < mapConfig.ScaleList.length; i++) {
            let scale = mapConfig.ScaleList[i];
            let res = GeoUtils.getResolution(scale);
            let levelScope = res * mapConfig.TileSize * this.TileNum;
            //这里乘以expectScopeFactor，是为了避免相机倾斜的时候因为相机高度较低，得到的地图层级较小，然后加载了过多的图片。为了避免这个问题，就把levelScope变小，这样可以得到稍大的地图层级
            levelScope *= expectScopeFactor;

            if (levelScope <= visualScope) {
                mapLevel = i;
                break;
            }
        }*/
        for (let i = 0; i < this.scopeArray.length; i++) {
            let levelScope = this.scopeArray[i];
            //这里乘以expectScopeFactor，是为了避免相机倾斜的时候因为相机高度较低，得到的地图层级较小，然后加载了过多的图片。为了避免这个问题，就把levelScope变小，这样可以得到稍大的地图层级
            levelScope *= expectScopeFactor;

            if (levelScope <= visualScope) {
                mapLevel = i;
                break;
            }
        }

        if(isConsiderLevelRange){
            if (mapLevel < minZoom) {
                mapLevel = minZoom;
            } else if (mapLevel > maxZoom) {
                mapLevel = maxZoom;
            }
        }
      
        return mapLevel;
    }

    private convertScreenToThree(screenX, screenY, ndcZ = -1.0) { //从屏幕坐标点转换到三维坐标点
        // Normalize the screen coordinates to the range [-1, 1]
        let normalizedX = (screenX / this.vituralViewportWidth) * 2 - 1;
        let normalizedY = -(screenY / this.vituralViewportHeight) * 2 + 1;

        // Create a 3D vector with the normalized screen coordinates
        let vector = new Vector3(normalizedX, normalizedY, ndcZ);

        vector.unproject(this.virturalCamera);
        return vector;
    }

    private getIntersetPoint(mapPlane: Plane, vector) {
        let ray: Ray = new Ray(this.virturalCamera.position.clone(), vector);
        let intersectionResult = new Vector3();
        intersectionResult = ray.intersectPlane(mapPlane, intersectionResult);
        return intersectionResult;
    }

    /**
     * 指定地图到特定的层级。调用该方法，相机会自动调整高度，使地图层级达到指定的层级，并且相机垂直于地图平面。
     * @param zoomParam 指定地图层级需要用到的参数
     * @param zoomParam.mapZoom 地图的层级
     * @param zoomParam.cameraLookAt 相机看向的位置
     * @param zoomParam.onCompleteCallback 地图层级调整完成后的回调函数
     */
    public setMapZoom(zoomParam: {mapZoom: number, cameraLookAt?: Point | Vector3, onCompleteCallback?}) {
        if(zoomParam.mapZoom < this.app.options.Map.minZoom || zoomParam.mapZoom > this.app.options.Map.maxZoom) {
            return;
        }
        
        let angle = this.app.options.Camera.cameraFov;
        let expectScopeFactor = Number(Math.cos(MathUtils.degToRad(angle)).toFixed(4));

        let targetScope = this.scopeArray[zoomParam.mapZoom + 1];
        targetScope *= expectScopeFactor;

        let cameraFovSin = Math.sin( MathUtils.degToRad(angle) );
        let cameraHeight = targetScope / cameraFovSin;

        let cameraPosition;
        if(zoomParam.cameraLookAt) {
            if(zoomParam.cameraLookAt instanceof Point){
                cameraPosition = zoomParam.cameraLookAt.toEPSGWeb();
            }else{
                cameraPosition = zoomParam.cameraLookAt.clone();
            }
        }else{
            cameraPosition = this.app.camera.lookAtTarget.clone();
        }

        let newCameraPosition = new Point(cameraPosition.x, cameraPosition.y, cameraHeight, Const.EPSGType.EPSGWeb);
        let newCameraLookat = new Point(cameraPosition.x, cameraPosition.y, 0, Const.EPSGType.EPSGWeb);
        
        let self = this;
        this.app.camera.flyTo(newCameraPosition, newCameraLookat, 100, zoomParam.onCompleteCallback);
    }

     /**
     * 缩放地图
     * @param isZoomIn true表示放大，false表示缩小
     */
    public zoomMap(isZoomIn: boolean) {
        if((!isZoomIn && this.app.map.zoom <= this.app.options.Map.minZoom)) {
            return;
        }
        if((isZoomIn && this.app.map.zoom >= this.app.options.Map.maxZoom)) {
            return;
        }
        let cameraPosition = this.app.camera.position.clone();
        let cameraLookAt = this.app.camera.lookAtTarget;
        let rayVector: Vector3 = cameraPosition.clone().sub(cameraLookAt);
        let rayLength = rayVector.length();
        let moveDistance = rayLength / 2;
        let normalizeRay: Vector3 = rayVector.normalize();   

        if(isZoomIn) {
            moveDistance *= -1;
        }

        let moveRay = normalizeRay.multiplyScalar(moveDistance);
        let newCameraPosition = cameraPosition.add(moveRay);

        newCameraPosition = new Point(newCameraPosition.x, newCameraPosition.y, newCameraPosition.z, Const.EPSGType.EPSGWeb);
        this.app.camera.flyTo(newCameraPosition, cameraLookAt, 100, null, true);
    }
    
    public setVisualScope(range: {startPoint: Point, endPoint: Point}, onCompleteCallback?){
        range.startPoint.toEPSGWeb();
        range.endPoint.toEPSGWeb();

        let verticalDistance = Math.abs(range.startPoint.y - range.endPoint.y) / 2;
        let horizontalDistance = Math.abs(range.startPoint.x - range.endPoint.x) /2;

        let cameraHeight;
        if(verticalDistance > horizontalDistance) {
            let angle = this.app.options.Camera.cameraFov / 2;
            let cameraFovSin = Math.sin( MathUtils.degToRad(angle) );
            cameraHeight = verticalDistance / cameraFovSin;
        }else{
            let angle = this.app.camera.cameraUtils.getHorizontalFov() / 2;
            let cameraFovSin = Math.sin( MathUtils.degToRad(angle) );
            cameraHeight = horizontalDistance / cameraFovSin;
        }

        let newCameraPosition = range.startPoint.clone().add(range.endPoint.clone()).multiplyScalar(0.5); //计算可能有误，需要修正
        newCameraPosition.z = cameraHeight;

        let newCameraLookat = new Point(newCameraPosition.x, newCameraPosition.y, 0, Const.EPSGType.EPSGWeb);
        
        this.app.camera.flyTo(newCameraPosition, newCameraLookat, 100, onCompleteCallback);
    }
}
export { MapLevelController }